package com.nervenets.web.redis.utils;

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.io.Resource;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisCommands;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
import java.util.regex.Pattern;

/**
 * JEDISCLUSTER生产工厂类
 *
 * @author Administrator
 */
public class JedisClusterFactory implements FactoryBean<JedisCommands>, InitializingBean {
    private static Logger logger = LoggerFactory.getLogger(JedisClusterFactory.class);
    //REDIS集群地址
    private Resource addressConfig;
    private String addressKeyPrefix;
    //被注入（生产）的jedisCluster实例
    private JedisCommands jedisCluster;
    //链接超时时间
    private Integer connectionTimeout;

    //操作超时时间
    private Integer timeout;
    //最大重定向次数
    private Integer maxRedirections;
    //连接池配置
    private GenericObjectPoolConfig genericObjectPoolConfig;
    //密码（内网集群化不用配置）
    private String password;

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    private Pattern p = Pattern.compile("^.+[:]\\d{1,5}\\s*$");

    @Override
    public JedisCommands getObject() throws Exception {
        return jedisCluster;
    }

    @Override
    public Class<? extends JedisCommands> getObjectType() {
        return (this.jedisCluster != null ? this.jedisCluster.getClass() : JedisCluster.class);
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

    //将从配置文件中读取出的HOST/PORT信息，转换为HOSTANDPORT
    private Set<HostAndPort> parseHostAndPort() throws Exception {
        try {
            Properties prop = new Properties();
            prop.load(this.addressConfig.getInputStream());

            Set<HostAndPort> nodes = new HashSet<>();
            for (Object key : prop.keySet()) {
                if (!((String) key).startsWith(addressKeyPrefix)) {
                    continue;
                }
                String val = (String) prop.get(key);
                boolean isIpPort = p.matcher(val).matches();

                if (!isIpPort) {
                    logger.error(String.format("[%s] 不合法", val));
                    continue;
                }
                String[] strings = val.split(":");
                logger.info(Arrays.toString(strings));
                HostAndPort hostAndPort = new HostAndPort(strings[0], Integer.parseInt(strings[1]));
                nodes.add(hostAndPort);
            }
            return nodes;
        } catch (IllegalArgumentException ex) {
            throw ex;
        } catch (Exception ex) {
            throw new Exception("解析 jedis 配置文件失败", ex);
        }
    }

    /**
     * 创建JedisCluster实例
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        Set<HostAndPort> hostAndPorts = this.parseHostAndPort();
        logger.info(String.format("JedisCluster init %s redis cluster nodes", hostAndPorts.size()));
        jedisCluster = new JedisCluster(hostAndPorts, connectionTimeout, timeout, maxRedirections, genericObjectPoolConfig);
    }

    public void setAddressConfig(Resource addressConfig) {
        this.addressConfig = addressConfig;
    }

    public void setTimeout(int timeout) {
        this.timeout = timeout;
    }

    public void setMaxRedirections(int maxRedirections) {
        this.maxRedirections = maxRedirections;
    }

    public void setAddressKeyPrefix(String addressKeyPrefix) {
        this.addressKeyPrefix = addressKeyPrefix;
    }

    public void setGenericObjectPoolConfig(GenericObjectPoolConfig genericObjectPoolConfig) {
        this.genericObjectPoolConfig = genericObjectPoolConfig;
    }

    public Integer getConnectionTimeout() {
        return connectionTimeout;
    }

    public void setConnectionTimeout(Integer connectionTimeout) {
        this.connectionTimeout = connectionTimeout;
    }
}
